#include "CEngine.h"


CList::CList()
{
	Clear();
};
CList::~CList()
{
	Clear();
};

void CList::Add(void *d)
{
	int i;
	for(i=0;i<254;i++)
		if(m_List[i].m_On==false)
	{
		m_List[i].m_On = true;
		m_List[i].m_Data = d;
		m_Length++;
		return;
	};
	
};
void CList::Delete(int n)
{
	int i;
	m_List[254].m_On = false;
	for(i=0;i<254;i++)
		if(i>n-1)
	{
		m_List[i].m_Data = m_List[i+1].m_Data;
		m_List[i].m_On = m_List[i+1].m_On;
	};
	m_Length--;
};
void *CList::Get(int n)
{
	if(m_List[n].m_On==true)
		return m_List[n].m_Data;
	return NULL;
};
void CList::Clear()
{
	int i;
	m_Length = 0;
	for(i=0;i<255;i++)
	{
		m_List[i].m_Data = NULL;
		m_List[i].m_On = false;
	};
};
int CList::Find(void *d)
{
	int i;
	for(i=0;i<255;i++)
		if(m_List[i].m_On&&m_List[i].m_Data==d)
			return i;
};
int CList::GetLength()
{
	return m_Length;
};

CTiles::CTiles()
{
	ClearTiles();
};
CTiles::~CTiles()
{
	ClearTiles();
};

void CTiles::LoadTile(char *s)
{
	char *b;
	b = (char *)malloc(1);
	b[0] = s[0];
	m_Tiles.Add(b);
};
void CTiles::ClearTiles()
{
	while(m_Tiles.GetLength()>0)
	{
		if(m_Tiles.Get(0)!=NULL)
			free((char *)m_Tiles.Get(0));
		m_Tiles.Delete(0);
	};
	m_Tiles.Clear();
};
void CTiles::DrawTile(int n,int x,int y)
{
	char *t;
	if(n>m_Tiles.GetLength()||n<0)
		n = 0;
	t = (char *)m_Tiles.Get(n);
	mvaddch(y, x, t[0]);
};

CObject::CObject()
{
	Clear();
};
CObject::~CObject()
{
	Clear();
};

void CObject::Run()
{
};
void CObject::Input(int e)
{
};
void CObject::Draw(int p)
{
	CEngine *e;
	if(m_DrawPass!=p)
		return;
	e = (CEngine *)GetEngine();
	e->m_Tiles.DrawTile(m_Tile,m_X-e->m_Room->m_CameraX,m_Y-e->m_Room->m_CameraY);
	if(m_Type==5)
		e->m_Game.m_SeenEnemies++;
};
void CObject::Clear()
{
	m_X = m_Y = m_Tile = m_Type = m_DrawPass = 0;
	m_Collide = m_Shown = false;
	m_Clear = true;
};
CObject *CObject::SpawnSelf(int x,int y)
{
	CObject *o;
	CEngine *e;
	e = (CEngine *)GetEngine();
	o = new CObject();
	o->m_X = x;
	o->m_Y = y;
	o->m_Collide = m_Collide;
	o->m_Shown = m_Shown;
	o->m_Clear = m_Clear;
	o->m_Type = m_Type;
	o->m_Tile = m_Tile;
	e->m_Room->m_Objects.Add(o);
	return o;
};

bool CObject::CollideMap(int x,int y)
{
	CEngine *e;
	if(x<0||y<0||x>MAPXSIZE-1||y>MAPYSIZE-1)
		return true;
	e = (CEngine *)GetEngine();
	if(e->m_Room->m_Map[x][y][1]==1)
		return true;
	return false;
};
CObject *CObject::CollideObject(int n)
{
	CEngine *e;
	CObject *o;
	int i,j,k;
	e = (CEngine *)GetEngine();
	j = e->m_Room->m_Objects.GetLength();
	k = 0;
	for(i=0;i<j;i++)
	{
		o = (CObject *)e->m_Room->m_Objects.Get(i);
		if(o!=this&&o->m_X==m_X&&o->m_Y==m_Y)
		{
			if(k==n)
				return o;
			k++;
		};
	};
	return NULL;
};
int CObject::GetType()
{
	return m_Type;
};
void CObject::Spawn(CObject *o)
{
	CEngine *e;
	e = (CEngine *)GetEngine();
	if(o==NULL)
		return;
	e->m_Room->m_Objects.Add(o);
};
void CObject::Die()
{
	CEngine *e;
	CObject *o;
	int i,j,k;
	e = (CEngine *)GetEngine();
	j = e->m_Room->m_Objects.GetLength();
	k = 0;
	for(i=0;i<j;i++)
	{
		o = (CObject *)e->m_Room->m_Objects.Get(i);
		if(o==this&&o->m_X==m_X)
		{
			e->m_Room->m_Objects.Delete(i);
			delete(this);
			return;
		};
	};
};
void CObject::Message(int m)
{
};

CRoom::CRoom()
{
	Clear();
};
CRoom::~CRoom()
{
	Clear();
};

void CRoom::Run()
{
	CObject *o,*p;
	CEngine *e;
	int i,j;
	e = (CEngine *)GetEngine();
	p = e->m_Game.GetPlayer();
	p->Run();
	for(i=0;i<m_Objects.GetLength();i++)
	{
		o = (CObject *)m_Objects.Get(i);
		if(o!=NULL&&o!=p&&e->m_On)
			o->Run();
	};
};
void CRoom::Input(int e)
{
	CObject *o;
	int i,j;
	for(i=0;i<m_Objects.GetLength();i++)
	{
		o = (CObject *)m_Objects.Get(i);
		o->Input(e);
	};
};
void CRoom::Draw()
{
	CEngine *e;
	CObject *o,*o2;
	int i,j,k,n;
	float x,y,l,x2,y2;
	bool br;
	e = (CEngine *)GetEngine();
	o = e->m_Game.GetPlayer();
	for(i=0;i<MAPXSIZE;i++)
		for(j=0;j<MAPYSIZE;j++)
			if(m_Map[i][j][2]==1)
				m_Map[i][j][2] = 2;
	for(i=0;i<MAPXSIZE+1;i++)//Objects should be able to block light or specify to not update a particular square until next seen
		for(j=0;j<MAPYSIZE+1;j++)
		{
			x = i-(0.5f+o->m_X);
			y = j-(0.5f+o->m_Y);
			l = sqrt((x*x)+(y*y));
			x/=l;
			y/=l;
			x2 = 0.5f+o->m_X;
			y2 = 0.5f+o->m_Y;
			for(k=0;k<(int)l+1;k++)
			{
				if((int)x2>MAPXSIZE-1||(int)y2>MAPYSIZE-1)
					break;
				m_Map[(int)x2][(int)y2][2] = 1;
				if(m_Map[(int)x2][(int)y2][1]!=0)
					break;
				br = false;
				for(n=0;n<m_Objects.GetLength();n++)
				{
					o2 = (CObject *)m_Objects.Get(n);
					if(o2->m_X==(int)x2&&o2->m_Y==(int)y2&&o2!=o&&!o2->m_Clear&&(o->m_X!=o2->m_X||o->m_Y!=o2->m_Y))
						br = true;
				};
				if(br==true)
					break;
				x2+=x;
				y2+=y;
			};
		};
	//The third array of map: 0 - never seen 1 - seen now 2 - seen before 3 - Don't update until seen again. Used for objects.
	for(i=0;i<80;i++)
		for(j=0;j<25;j++)
			if(m_Map[i+m_CameraX][j+m_CameraY][2]!=0)
			{
				if(m_Map[i+m_CameraX][j+m_CameraY][2]==2)
					attron(COLOR_PAIR(2));
				e->m_Tiles.DrawTile(m_Map[i+m_CameraX][j+m_CameraY][0],i,j);
				if(m_Map[i+m_CameraX][j+m_CameraY][2]==2)
					attron(COLOR_PAIR(1));
			}
			else
				mvaddch(j,i,' ');
		
	j = m_Objects.GetLength();
	e->m_Game.m_SeenEnemies = 0;
	for(k=0;k<4;k++)
		for(i=0;i<j;i++)
		{
			o = (CObject *)m_Objects.Get(i);
			if(m_Map[o->m_X][o->m_Y][2]==1)
				o->Draw(k);
		};
};
void CRoom::CenterCamera(int x,int y)
{
	m_CameraX = 0;//x-MAPXSIZE/2;
	m_CameraY = 0;//y-MAPYSIZE/2;
};
void CRoom::Clear()
{
	int i,j;
	for(i=0;i<MAPXSIZE;i++)
		for(j=0;j<MAPYSIZE;j++)
			m_Map[i][j][0] = m_Map[i][j][1] = m_Map[i][j][2] = 0;
	while(m_Objects.GetLength()>0)
	{
		if(m_Objects.Get(0)!=NULL)
			delete((CObject *)m_Objects.Get(0));
		m_Objects.Delete(0);
	};
	CenterCamera(0.0f,0.0f);
};

CGUIText::CGUIText(char *t,bool m)
{
	strncpy(m_Text,t,80);
	m_More = m;
};
CGUIText::~CGUIText()
{
};

CGUI::CGUI()
{
	ClearBottom();
	ClearTop();
};
CGUI::~CGUI()
{
	ClearBottom();
	ClearTop();
};

void CGUI::Draw()
{
	DrawBottom();
	DrawTop();
	ClearTop();
	ClearBottom();
};
void CGUI::DrawBottom()
{
	int i,j,x,x2;
	j = m_BottomText.GetLength();
	x = 0;
	for(i=0;i<j;i++)
	{
		x2=x+strlen(((CGUIText *)m_BottomText.Get(i))->m_Text);
		if(x2<80)
			mvprintw(24,x,((CGUIText *)m_BottomText.Get(i))->m_Text);
		else
			return;
		x=x2;
	};
};
void CGUI::DrawTop()
{
	int i,j,x,x2,m;
	j = m_TopText.GetLength();
	x = m = 0;
	for(i=0;i<j;i++)
	{

		x2=x+strlen(((CGUIText *)m_TopText.Get(i))->m_Text);
		if(x2<80)
			mvprintw(0,x,((CGUIText *)m_TopText.Get(i))->m_Text);
		else if(((CGUIText *)m_TopText.Get(i))->m_More==true)
		{
			if(m>j+2)
				return;
			mvprintw(0,74,"[more]\n");
			getch();
			refresh();
			x=x2=0;
			i--;
			m++;
			mvprintw(0,0,"\n");
		}
		else
			return;
		x=x2;
	};
};

void CGUI::ClearBottom()
{
	CGUIText *t;
	while(m_BottomText.GetLength()>0)
	{
		t = (CGUIText *)m_BottomText.Get(0);
		if(t!=NULL)
			delete(t);
		m_BottomText.Delete(0);
	};
};
void CGUI::ClearTop()
{
	CGUIText *t;
	while(m_TopText.GetLength()>0)
	{
		t = (CGUIText *)m_TopText.Get(0);
		if(t!=NULL)
			delete(t);
		m_TopText.Delete(0);
	};
};
void CGUI::AddBottom(char *s)
{
	CGUIText *t;
	t = new CGUIText(s,false);
	t->m_Text[0] = toupper(t->m_Text[0]);
	m_BottomText.Add(t);
};
void CGUI::AddTop(char *s,bool m)
{
	CGUIText *t;
	t = new CGUIText(s,m);
	t->m_Text[0] = toupper(t->m_Text[0]);
	m_TopText.Add(t);
};


CEngine::CEngine()
{
	initscr();
	cbreak();
	noecho();
	nl();
	keypad(stdscr,true);
	srand(time(0));
	start_color();
	init_pair(1, COLOR_WHITE, COLOR_BLACK);
	init_pair(2, COLOR_BLUE, COLOR_BLACK);
	attron(COLOR_PAIR(1));
	
	SetEngine(this);
	m_On = true;
	m_Game.Load();
};
CEngine::~CEngine()
{
	endwin();
	
	m_On = false;
};

bool CEngine::Run()
{
	m_Game.Run();
	m_Room->Run();
	Draw();
	m_GUI.Draw();
	Input();
	return m_On;
};
void CEngine::Input()
{
	int e;
	flushinp();
	e = getch();
	switch(e)
	{
		case 'q':
			m_On = false;
			break;
	};
	m_Room->Input(e);
};
void CEngine::Draw()
{
	m_Room->Draw();
	refresh();
};
